namespace GYLite
{
    export class Transform3D extends egret.EventDispatcher
    {   
        public static _tempVector30 = new Vector3;
        public static _tempVector31 = new Vector3;
        public static _tempVector32 = new Vector3;
        public static _tempVector33 = new Vector3;
        
        public static _tempQuaternion0 = new Quaternion;
        public static _tempMatrix0 = new Matrix4x4;

        public static _angleToRandin:number = 180 / Math.PI;
        private _localQuaternionUpdate:boolean;
        private _locaEulerlUpdate:boolean;
        private _localUpdate:boolean;
        private _worldUpdate:boolean;
        private _positionUpdate:boolean;
        private _rotationUpdate:boolean;
        private _scaleUpdate:boolean;
        private _parent:Transform3D;        
        // private _dummy;
        private _localPosition:Vector3;
        private _localRotation:Quaternion;
        private _localScale:Vector3;
        private _localRotationEuler:Vector3;
        private _localMatrix:Matrix4x4;
        private _position:Vector3;
        private _rotation:Quaternion;
        private _scale:Vector3;
        private _worldMatrix:Matrix4x4;
        private _forward:Vector3;
        private _up:Vector3;
        private _right:Vector3;
        private _owner:any;
        private _childs:Transform3D[]
         /**变换中心点,注意:该中心点不受变换的影响。*/
         public pivot;
        constructor(owner) {           
            
            super();
            this._localPosition = new Vector3();
            this._localRotation = new Quaternion(0, 0, 0, 1);
            this._localScale = new Vector3(1, 1, 1);
            this._localRotationEuler = new Vector3();
            this._localMatrix = new Matrix4x4();
            this._position = new Vector3();
            this._rotation = new Quaternion(0, 0, 0, 1);
            this._scale = new Vector3(1, 1, 1);
            this._worldMatrix = new Matrix4x4();
            this._forward = new Vector3();
            this._up = new Vector3();
            this._right = new Vector3();
            this._owner = owner;
            this._childs = [];
        }
        /**
         *@private
        */
        private _updateLocalMatrix() {
            if (this.pivot && (this.pivot.x !== 0 || this.pivot.y !== 0 || this.pivot.z !== 0)) {
                var scalePivot = Transform3D._tempVector30;
                Vector3.multiply(this.pivot, this._localScale, scalePivot);
                var scaleOffsetPosition = Transform3D._tempVector31;
                Vector3.subtract(scalePivot, this.pivot, scaleOffsetPosition);
                var rotationOffsetPosition = Transform3D._tempVector32;
                var localRot = this.localRotation;
                Vector3.transformQuat(scalePivot, localRot, rotationOffsetPosition);
                Vector3.subtract(rotationOffsetPosition, scalePivot, rotationOffsetPosition);
                var resultLocalPosition = Transform3D._tempVector33;
                Vector3.subtract(this._localPosition, scaleOffsetPosition, resultLocalPosition);
                Vector3.subtract(resultLocalPosition, rotationOffsetPosition, resultLocalPosition);
                Matrix4x4.createAffineTransformation(resultLocalPosition, localRot, this._localScale, this._localMatrix);
            } else {
                Matrix4x4.createAffineTransformation(this._localPosition, this.localRotation, this._localScale, this._localMatrix);
            }
        }

        /**
         *@private
        */
        private _onWorldPositionRotationTransform = function () {
            if (!this._worldUpdate || !this._positionUpdate || !this._rotationUpdate) {
                this._worldUpdate = this._positionUpdate = this._rotationUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldPositionRotationTransform();
            }
        }

        /**
         *@private
        */
        private _onWorldPositionScaleTransform = function () {
            if (!this._worldUpdate || !this._positionUpdate || !this._scaleUpdate) {
                this._worldUpdate = this._positionUpdate = this._scaleUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldPositionScaleTransform();
            }
        }

        /**
         *@private
        */
        private _onWorldPositionTransform = function () {
            if (!this._worldUpdate || !this._positionUpdate) {
                this._worldUpdate = this._positionUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldPositionTransform();
            }
        }

        /**
         *@private
        */
        private _onWorldRotationTransform = function () {
            if (!this._worldUpdate || !this._rotationUpdate) {
                this._worldUpdate = this._rotationUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldPositionRotationTransform();
            }
        }

        /**
         *@private
        */
        private _onWorldScaleTransform = function () {
            if (!this._worldUpdate || !this._scaleUpdate) {
                this._worldUpdate = this._scaleUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldPositionScaleTransform();
            }
        }

        /**
         *@private
        */
        private _onWorldTransform = function () {
            if (!this._worldUpdate || !this._positionUpdate || !this._rotationUpdate || !this._scaleUpdate) {
                this._worldUpdate = this._positionUpdate = this._rotationUpdate = this._scaleUpdate = true;
                this.event(/*laya.events.Event.WORLDMATRIX_NEEDCHANGE*/"worldmatrixneedchanged");
                for (var i = 0, n = this._childs.length; i < n; i++)
                    this._childs[i]._onWorldTransform();
            }
        }

        /**
         *平移变换。
        *@param translation 移动距离。
        *@param isLocal 是否局部空间。
        */
        public translate = function (translation, isLocal) {
            (isLocal === void 0) && (isLocal = true);
            if (isLocal) {
                Matrix4x4.createFromQuaternion(this.localRotation, Transform3D._tempMatrix0);
                Vector3.transformCoordinate(translation, Transform3D._tempMatrix0, Transform3D._tempVector30);
                Vector3.add(this.localPosition, Transform3D._tempVector30, this._localPosition);
                this.localPosition = this._localPosition;
            } else {
                Vector3.add(this.position, translation, this._position);
                this.position = this._position;
            }
        }

        /**
         *旋转变换。
        *@param rotations 旋转幅度。
        *@param isLocal 是否局部空间。
        *@param isRadian 是否弧度制。
        */
        public rotate = function (rotation, isLocal, isRadian) {
            (isLocal === void 0) && (isLocal = true);
            (isRadian === void 0) && (isRadian = true);
            var rot;
            if (isRadian) {
                rot = rotation;
            } else {
                Vector3.scale(rotation, Math.PI / 180.0, Transform3D._tempVector30);
                rot = Transform3D._tempVector30;
            }
            Quaternion.createFromYawPitchRoll(rot.y, rot.x, rot.z, Transform3D._tempQuaternion0);
            if (isLocal) {
                Quaternion.multiply(this._localRotation, Transform3D._tempQuaternion0, this._localRotation);
                this.localRotation = this._localRotation;
            } else {
                Quaternion.multiply(Transform3D._tempQuaternion0, this.rotation, this._rotation);
                this.rotation = this._rotation;
            }
        }

        /**
         *观察目标位置。
        *@param target 观察目标。
        *@param up 向上向量。
        *@param isLocal 是否局部空间。
        */
        public lookAt = function (target, up, isLocal) {
            (isLocal === void 0) && (isLocal = false);
            var targetE = target.elements;
            var eyeE;
            if (isLocal) {
                eyeE = this._localPosition.elements;
                if (Math.abs(eyeE[0] - targetE[0]) < MathUtils3D.zeroTolerance && Math.abs(eyeE[1] - targetE[1]) < MathUtils3D.zeroTolerance && Math.abs(eyeE[2] - targetE[2]) < MathUtils3D.zeroTolerance)
                    return;
                Quaternion.lookAt(this._localPosition, target, up, this._localRotation);
                this._localRotation.invert(this._localRotation);
                this.localRotation = this._localRotation;
            } else {
                var worldPosition = this.position;
                eyeE = worldPosition.elements;
                if (Math.abs(eyeE[0] - targetE[0]) < MathUtils3D.zeroTolerance && Math.abs(eyeE[1] - targetE[1]) < MathUtils3D.zeroTolerance && Math.abs(eyeE[2] - targetE[2]) < MathUtils3D.zeroTolerance)
                    return;
                Quaternion.lookAt(worldPosition, target, up, this._rotation);
                this._rotation.invert(this._rotation);
                this.rotation = this._rotation;
            }
        }

        /**
         *@private
        */
        public get _isFrontFaceInvert():boolean {
            var scale = this.scale;
            var isInvert = scale.x < 0;
            (scale.y < 0) && (isInvert = !isInvert);
            (scale.z < 0) && (isInvert = !isInvert);
            return isInvert;
        }

        /**
         *获取所属精灵。
        */
        public get owner():any {
            return this._owner;
        }

        /**
         *设置局部旋转。
        *@param value 局部旋转。
        */
        /**
         *获取局部旋转。
        *@return 局部旋转。
        */
        public get localRotation() {
            if (this._localQuaternionUpdate) {
                var eulerE = this._localRotationEuler.elements;
                Quaternion.createFromYawPitchRoll(eulerE[1] / Transform3D._angleToRandin, eulerE[0] / Transform3D._angleToRandin, eulerE[2] / Transform3D._angleToRandin, this._localRotation);
            }
            return this._localRotation;
        }
        public set localRotation(value) {
            this._localRotation = value;
            this._localRotation.normalize(this._localRotation);
            this._locaEulerlUpdate = true;
            this._localQuaternionUpdate = false;
            this._localUpdate = true;
            if (this.pivot && (this.pivot.x !== 0 || this.pivot.y !== 0 || this.pivot.z !== 0))
                this._onWorldPositionRotationTransform();
            else
                this._onWorldRotationTransform();
        }

        /**
         *设置世界矩阵。
        *@param value 世界矩阵。
        */
        /**
         *获取世界矩阵。
        *@return 世界矩阵。
        */
        public get worldMatrix() {
            if (this._worldUpdate) {
                if (this._parent != null)
                    Matrix4x4.multiply(this._parent.worldMatrix, this.localMatrix, this._worldMatrix);
                else
                    this.localMatrix.cloneTo(this._worldMatrix);
                this._worldUpdate = false;
            }
            return this._worldMatrix;
        }
        public set worldMatrix(value) {
            if (this._parent === null) {
                value.cloneTo(this._localMatrix);
            } else {
                this._parent.worldMatrix.invert(this._localMatrix);
                Matrix4x4.multiply(this._localMatrix, value, this._localMatrix);
            }
            this.localMatrix = this._localMatrix;
            this._worldMatrix = value;
            this._worldUpdate = false;
        }

        /**
         *获取世界矩阵是否需要更新。
        *@return 世界矩阵是否需要更新。
        */
        public worldNeedUpdate() {
            return this._worldUpdate;
        }

        /**
         *设置局部矩阵。
        *@param value 局部矩阵。
        */
        /**
         *获取局部矩阵。
        *@return 局部矩阵。
        */
        public get localMatrix() {
            if (this._localUpdate) {
                this._updateLocalMatrix();
                this._localUpdate = false;
            }
            return this._localMatrix;
        }
        public set localMatrix(value) {
            this._localMatrix = value;
            this._localMatrix.decomposeTransRotScale(this._localPosition, this._localRotation, this._localScale);
            this._localUpdate = false;
            this._onWorldTransform();
        }

        /**
         *设置关联虚拟变换。
        *@param value 虚拟变换。
        */
        /**
         *获取关联虚拟变换。
        *@return 虚拟变换。
        */
        // public get dummy():Transform3D {
        //     return this._dummy;
        // }
        // public set dummy(value:Transform3D) {
        //     if (this._dummy !== value) {
        //         (this._dummy) && (this._dummy._entity = null);
        //         (value) && (value._entity = this);
        //         this._dummy = value;
        //     }
        // }

        /**
         *设置局部位置。
        *@param value 局部位置。
        */
        /**
         *获取局部位置。
        *@return 局部位置。
        */
        public get localPosition() {
            return this._localPosition;
        }
        public set localPosition(value) {
            this._localPosition = value;
            this._localUpdate = true;
            this._onWorldPositionTransform();
        }

        /**
         *设置世界位置。
        *@param value 世界位置。
        */
        /**
         *获取世界位置。
        *@return 世界位置。
        */
        public get position() {
            if (this._positionUpdate) {
                if (this._parent != null) {
                    var parentPosition = this._parent.position;
                    Vector3.multiply(this._localPosition, this._parent.scale, Transform3D._tempVector30);
                    Vector3.transformQuat(Transform3D._tempVector30, this._parent.rotation, Transform3D._tempVector30);
                    Vector3.add(parentPosition, Transform3D._tempVector30, this._position);
                } else {
                    this._localPosition.cloneTo(this._position);
                }
                this._positionUpdate = false;
            }
            return this._position;
        }
        public set position(value) {
            if (this._parent != null) {
                Vector3.subtract(value, this._parent.position, this._localPosition);
                var parentScaleE = this._parent.scale.elements;
                var psX = parentScaleE[0], psY = parentScaleE[1], psZ = parentScaleE[2];
                if (psX !== 1.0 || psY !== 1.0 || psZ !== 1.0) {
                    var invertScale = Transform3D._tempVector30;
                    var invertScaleE = invertScale.elements;
                    invertScaleE[0] = 1.0 / psX;
                    invertScaleE[1] = 1.0 / psY;
                    invertScaleE[2] = 1.0 / psZ;
                    Vector3.multiply(this._localPosition, invertScale, this._localPosition);
                }
                ;
                var parentRotation = this._parent.rotation;
                parentRotation.invert(Transform3D._tempQuaternion0);
                Vector3.transformQuat(this._localPosition, Transform3D._tempQuaternion0, this._localPosition);
            } else {
                value.cloneTo(this._localPosition);
            }
            this.localPosition = this._localPosition;
            this._position = value;
            this._positionUpdate = false;
        }

        /**
         *设置局部缩放。
        *@param value 局部缩放。
        */
        /**
         *获取局部缩放。
        *@return 局部缩放。
        */
        public get localScale() {
            return this._localScale;
        }
        public set localScale(value) {
            this._localScale = value;
            this._localUpdate = true;
            if (this.pivot && (this.pivot.x !== 0 || this.pivot.y !== 0 || this.pivot.z !== 0))
                this._onWorldPositionScaleTransform();
            else
                this._onWorldScaleTransform();
        }

        /**
         *设置局部空间的旋转角度。
        *@param value 欧拉角的旋转值，顺序为x、y、z。
        */
        /**
         *获取局部空间的旋转角度。
        *@return 欧拉角的旋转值，顺序为x、y、z。
        */
        public get localRotationEuler():Vector3 {
            if (this._locaEulerlUpdate) {
                this._localRotation.getYawPitchRoll(Transform3D._tempVector30);
                var eulerE = Transform3D._tempVector30.elements;
                var localRotationEulerE = this._localRotationEuler.elements;
                localRotationEulerE[0] = eulerE[1] * Transform3D._angleToRandin;
                localRotationEulerE[1] = eulerE[0] * Transform3D._angleToRandin;
                localRotationEulerE[2] = eulerE[2] * Transform3D._angleToRandin;
            }
            return this._localRotationEuler;
        }
        public set localRotationEuler(value:Vector3) {
            this._localRotationEuler = value;
            this._locaEulerlUpdate = false;
            this._localQuaternionUpdate = true;
            this._localUpdate = true;
            if (this.pivot && (this.pivot.x !== 0 || this.pivot.y !== 0 || this.pivot.z !== 0))
                this._onWorldPositionRotationTransform();
            else
                this._onWorldRotationTransform();
        }

        /**
         *设置世界旋转。
        *@param value 世界旋转。
        */
        /**
         *获取世界旋转。
        *@return 世界旋转。
        */
        public get rotation() {
            if (this._rotationUpdate) {
                if (this._parent != null)
                    Quaternion.multiply(this._parent.rotation, this.localRotation, this._rotation);
                else
                    this.localRotation.cloneTo(this._rotation);
                this._rotationUpdate = false;
            }
            return this._rotation;
        }
        public set rotation(value:Quaternion) {
            if (this._parent != null) {
                this._parent.rotation.invert(Transform3D._tempQuaternion0);
                Quaternion.multiply(value, Transform3D._tempQuaternion0, this._localRotation);
            } else {
                value.cloneTo(this._localRotation);
            }
            this.localRotation = this._localRotation;
            this._rotation = value;
            this._rotationUpdate = false;
        }

        /**
         *设置世界缩放。
        *@param value 世界缩放。
        */
        /**
         *获取世界缩放。
        *@return 世界缩放。
        */
        public get scale() {
            if (!this._scaleUpdate)
                return this._scale;
            if (this._parent !== null)
                Vector3.multiply(this._parent.scale, this._localScale, this._scale);
            else
                this._localScale.cloneTo(this._scale);
            this._scaleUpdate = false;
            return this._scale;
        }
        public set scale(value:Vector3) {
            if (this._parent !== null) {
                var pScaleE = this._parent.scale.elements;
                var invPScaleE = Transform3D._tempVector30.elements;
                invPScaleE[0] = 1.0 / pScaleE[0];
                invPScaleE[1] = 1.0 / pScaleE[1];
                invPScaleE[2] = 1.0 / pScaleE[2];
                Vector3.multiply(value, Transform3D._tempVector30, this._localScale);
            } else {
                value.cloneTo(this._localScale);
            }
            this.localScale = this._localScale;
            this._scale = value;
            this._scaleUpdate = false;
        }

        /**
         *设置局部空间的旋转角度。
        *@param 欧拉角的旋转值，顺序为x、y、z。
        */
        public set rotationEuler(value:Vector3) {
            Quaternion.createFromYawPitchRoll(value.y, value.x, value.z, this._rotation);
            this.rotation = this._rotation;
        }

        /**
         *获取向前方向。
        *@return 向前方向。
        */
        public get forward():Vector3 {
            var worldMatElem = this.worldMatrix.elements;
            this._forward.elements[0] = -worldMatElem[8];
            this._forward.elements[1] = -worldMatElem[9];
            this._forward.elements[2] = -worldMatElem[10];
            return this._forward;
        }

        /**
         *获取向上方向。
        *@return 向上方向。
        */
        public get up():Vector3 {
            var worldMatElem = this.worldMatrix.elements;
            this._up.elements[0] = worldMatElem[4];
            this._up.elements[1] = worldMatElem[5];
            this._up.elements[2] = worldMatElem[6];
            return this._up;
        }

        /**
         *获取向右方向。
        *@return 向右方向。
        */
        public get right():Vector3 {
            var worldMatElem = this.worldMatrix.elements;
            this._right.elements[0] = worldMatElem[0];
            this._right.elements[1] = worldMatElem[1];
            this._right.elements[2] = worldMatElem[2];
            return this._right;
        }

        /**
         *设置父3D变换。
        *@param value 父3D变换。
        */
        /**
         *获取父3D变换。
        *@return 父3D变换。
        */
        public get parent() {
            return this._parent;
        }
        public set parent(value) {
            if (this._parent !== value) {
                if (this._parent) {
                    var parentChilds = this._parent._childs;
                    var index = parentChilds.indexOf(this);
                    parentChilds.splice(index, 1);
                }
                if (value) {
                    value._childs.push(this);
                    (value) && (this._onWorldTransform());
                }
                this._parent = value;
            }
        }
    }
}